package com.future.orm.session;

import com.future.orm.executor.Executor;
import com.future.orm.executor.SimpleExecutor;
import com.future.orm.mapping.MappedStatement;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.sql.SQLException;
import java.util.List;

/**
 * 默认SqlSession实现
 *
 * @author shiyong
 * 2020/5/20 10:38
 */
public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;

    private Executor executor;

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
        this.executor = new SimpleExecutor();
    }

    /**
     * 查询返回多条记录
     *
     * @param statementId 查询语句ID
     * @param param       查询参数
     * @return java.util.List<E>
     * @author shiyong
     * 2020/5/26 15:07
     */
    @Override
    public <E> List<E> selectList(String statementId, Object... param) throws Exception {
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);

        if (mappedStatement == null) {
            throw new Exception("获取查询语句失败！");
        }

        return executor.query(configuration, mappedStatement, param);
    }

    /**
     * 查询返回单条记录
     *
     * @param statementId 查询语句ID
     * @param param       查询参数
     * @return T
     * @author shiyong
     * 2020/5/26 15:08
     */
    @Override
    public <T> T selectOne(String statementId, Object... param) throws Exception {
        List<T> list = selectList(statementId, param);

        if (list.size() == 0) {
            return null;
        } else if (list.size() == 1) {
            return list.get(0);
        } else {
            throw new RuntimeException("返回多条记录！");
        }
    }

    /**
     * 插入记录
     *
     * @param statementId SQL语句ID
     * @param param       插入数据
     * @return int
     * @author shiyong
     * 2020/6/12 8:10
     */
    @Override
    public int insert(String statementId, Object... param) throws Exception {
        return update(statementId, param);
    }

    /**
     * 更新记录
     *
     * @param statementId SQL语句ID
     * @param param       更新数据
     * @return int
     * @author shiyong
     * 2020/6/12 8:10
     */
    @Override
    public int update(String statementId, Object... param) throws Exception {
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);

        if (mappedStatement == null) {
            throw new Exception("获取执行语句失败！");
        }

        return executor.update(configuration, mappedStatement, param);
    }

    /**
     * 删除记录
     *
     * @param statementId SQL语句ID
     * @param param       删除参数
     * @return int
     * @author shiyong
     * 2020/6/12 8:13
     */
    @Override
    public int delete(String statementId, Object... param) throws Exception {
        return update(statementId, param);
    }

    /**
     * 提交结果
     *
     * @author shiyong
     * 2020/6/12 8:14
     */
    @Override
    public void commit() throws SQLException {
        executor.commit();
    }

    /**
     * 回退
     *
     * @author shiyong
     * 2020/6/12 8:14
     */
    @Override
    public void rollback() throws SQLException {
        executor.rollback();
    }

    /**
     * 关闭数据库连接
     *
     * @author shiyong
     * 2020/6/14 13:43
     */
    @Override
    public void close() throws SQLException {
        executor.close();
    }

    /**
     * 获取Mapper接口代理类
     *
     * @param mapperClass Mapper接口
     * @return T
     * @author shiyong
     * 2020/6/14 14:06
     */
    @Override
    public <T> T getMapper(Class<?> mapperClass) {
        Object o = Proxy.newProxyInstance(mapperClass.getClassLoader(), new Class[]
            {mapperClass}, (proxy, method, args) -> {
                String methodName = method.getName();
                String className = method.getDeclaringClass().getName();

                // statementId
                String statementId = className + "." + methodName;
                MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);

                if (mappedStatement == null) {
                    throw new Exception("获取执行语句失败！");
                }

                switch (mappedStatement.getSqlCommandType()) {
                    case SELECT: {
                        Type genericReturnType = method.getGenericReturnType();

                        if (genericReturnType instanceof ParameterizedType) {
                            return selectList(statementId, args);
                        }

                        return selectOne(statementId, args);
                    }
                    case INSERT: {
                        return insert(statementId, args);
                    }
                    case UPDATE: {
                        return update(statementId, args);
                    }
                    case DELETE: {
                        return delete(statementId, args);
                    }
                    default: {
                        throw new Exception("SQL语句类型异常，" + statementId);
                    }
                }
            });

        return (T) o;
    }
}
